package com.ruoyi.search.controller;

import com.alibaba.fastjson.JSONArray;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.search.client.CmsClient;
import com.ruoyi.search.domain.Item;
import io.swagger.models.auth.In;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/search")
@CrossOrigin
public class SearchController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private CmsClient cmsClient;

    @GetMapping("/list")
    public Map search(String keyword, String ev, Integer psort, Integer page) throws IOException {
        //  SearchRequest(  SearchSourceBuilder(  QueryBuilder条件   )   )
        //创建一个搜索请求
        SearchRequest searchRequest = new SearchRequest("2112a");
        //设置请求的库名和表名
        searchRequest.types("goods");

        //创建一个SearchSourceBuilder对象，用来装搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //开启高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.field("brand");
        highlightBuilder.field("category");


        highlightBuilder.preTags("<bw>");
        highlightBuilder.postTags("</bw>");
        searchSourceBuilder.highlighter(highlightBuilder);




        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //构建搜索条件
        //1、对keyword做多字段匹配查询（name、brand、category）
        MultiMatchQueryBuilder multiMatchQueryBuilder =
                QueryBuilders.multiMatchQuery(keyword, "name", "brand", "category");
//                        .minimumShouldMatch("10%")
//                        .field("name",10)
//                        .field("brand",1)
//                        .field("category",1);
        boolQueryBuilder.must(multiMatchQueryBuilder);


        //2、对所有符合关键字的商品按品牌进行分组查询
        TermsAggregationBuilder brandAggr = AggregationBuilders.terms("brand_group").field("brand");
        TermsAggregationBuilder categoryAggr = AggregationBuilders.terms("category_group").field("category");
        searchSourceBuilder.aggregation(brandAggr);
        searchSourceBuilder.aggregation(categoryAggr);




        //判断前端是否传递了查询条件
        if(ev!=null){
            String[] split = ev.split("\\^");
            for(String param : split){
                String[] kv = param.split("_");
                if(kv[0].equals("exBrand")){
                    //添加品牌查询条件
                    TermQueryBuilder termQuery = QueryBuilders.termQuery("brand", kv[1]);
                    boolQueryBuilder.must(termQuery);
                    continue;
                }

                if(kv[0].equals("category")){
                    //添加品牌查询条件
                    TermQueryBuilder termQuery = QueryBuilders.termQuery("category", kv[1]);
                    boolQueryBuilder.must(termQuery);
                    continue;
                }

                if(kv[0].equals("exprice")){

                    if(kv[1].contains("gt")){
                        //500gt
                        Integer price1 = Integer.parseInt(kv[1].substring(0,kv[1].length()-2));
                        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price").gte(price1);
                        boolQueryBuilder.must(queryBuilder);
                    }else if(kv[1].startsWith("0")){
                        // 0-1622
                        Integer price2 = Integer.parseInt(kv[1].substring(2));
                        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price").lte(price2);
                        boolQueryBuilder.must(queryBuilder);
                    }else{
                        //868-1622
                        String[] priceArray = kv[1].split("-");
                        Integer price1  = Integer.parseInt(priceArray[0]);
                        Integer price2  = Integer.parseInt(priceArray[1]);
                        RangeQueryBuilder queryBuilder1 = QueryBuilders.rangeQuery("price").gte(price1);
                        RangeQueryBuilder queryBuilder2 = QueryBuilders.rangeQuery("price").lte(price2);
                        boolQueryBuilder.must(queryBuilder1);
                        boolQueryBuilder.must(queryBuilder2);
                    }

                    continue;
                }


                TermQueryBuilder termQuery = QueryBuilders.termQuery("spec."+kv[0]+".keyword", kv[1]);
                boolQueryBuilder.must(termQuery);
            }

        }

        //判断psort是否为空，不为空增加排序条件
        if(psort!=null){

            if(psort == 1){
                searchSourceBuilder.sort("price", SortOrder.DESC);
            }else if(psort == 2){
                searchSourceBuilder.sort("price", SortOrder.ASC);
            }else if(psort == 5){
                searchSourceBuilder.sort("createTime", SortOrder.DESC);
            }

        }

        //判断page是否为空，不为空增加分页条件
        if(page != null){
            Integer from = (page-1)*5;
            searchSourceBuilder.from(from);
        }
        //每页5条
        searchSourceBuilder.size(5);


        //创建一个SearchSourceBuilder对象
        searchSourceBuilder.query(boolQueryBuilder);


        //将查询条件装入搜索请求
        searchRequest.source(searchSourceBuilder);

        //执行搜索请求获得搜索结果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //解析查询结果获取分组数据
        Aggregations aggregations = searchResponse.getAggregations();
        //品牌分组数据
        List<Map> brandList = new ArrayList<>();
        ParsedStringTerms brandGroup = aggregations.get("brand_group");
        List<? extends Terms.Bucket> buckets = brandGroup.getBuckets();
        for(Terms.Bucket brand : buckets){
            String brandName = brand.getKeyAsString();
            Map<Object, Object> brandCache = stringRedisTemplate.opsForHash().entries("brand:" + brandName);
            if(brandCache.isEmpty()){
                brandCache = cmsClient.get(brandName);
                stringRedisTemplate.opsForHash().putAll("brand:"+brandName, brandCache);
            }
            brandList.add(brandCache);
        }
        //类型分组数据
        List<String> categoryList = new ArrayList<>();
        ParsedStringTerms categoryGroup = aggregations.get("category_group");
        List<? extends Terms.Bucket> categoryBuckets = categoryGroup.getBuckets();
        for(Terms.Bucket category : categoryBuckets){
            String categoryName = category.getKeyAsString();
            categoryList.add(categoryName);
        }

        //根据类型分组数据，查询出第一个类型的规格及规格参数
        String categoryName = categoryList.get(0);
        Long templateId = cmsClient.getTemplateId(categoryName);
        AjaxResult templateResult = cmsClient.getInfo(templateId);
        Map template = (Map) templateResult.get("data");
        String specIds = (String) template.get("specIds");
        List<Map> specList = JSONArray.parseArray(specIds,Map.class);

        for(Map spec : specList){
            Integer specId = (Integer) spec.get("id");
            List<String> options = redisTemplate.opsForList().range("spec:option:" + specId, 0, -1);
            spec.put("options",options);
        }

        //取总条数
        SearchHits hits = searchResponse.getHits();
        long total = hits.getTotalHits().value;
        System.out.println("查询到了:"+total+"条数据");

        List<Item> list = new ArrayList<>();

        //将查询到的结果封装成集合返回给前端
        SearchHit[] hits1 = hits.getHits();
        for(SearchHit obj : hits1){
            Item item = new Item();
            item.setId(obj.getId());
            Map<String, Object> sourceAsMap = obj.getSourceAsMap();

            //获取高亮名称
            Map<String, HighlightField> highlightFields = obj.getHighlightFields();
            HighlightField highlightField = highlightFields.get("name");

            if(highlightField != null){
                Text fragment = highlightField.getFragments()[0];
                String hlName = fragment.string();
                item.setName(hlName);
            }else{
                item.setName((String) sourceAsMap.get("name"));
            }


            item.setPrice(new BigDecimal((Double) sourceAsMap.get("price")));
            item.setUrl((String) sourceAsMap.get("url"));
            list.add(item);
        }

        Map result = new HashMap();
        result.put("list", list);
        result.put("total",total);
        result.put("brands",brandList);
        result.put("categories", categoryList);
        result.put("specList",specList);

        return result;
    }

}
